package dts_tcp

import (
	"context"
	"encoding/binary"
	"errors"
	"fmt"
	"github.com/zing-dev/go-bit-bytes/bit"
	"net"
	"sync"
	"sync/atomic"
	"time"
)

type ServerConfig struct {
	*Config
	MQTT *MQTT // MQTT配置
}

type ServerStatus struct {
	Running bool      `json:"running"`
	StartAt time.Time `json:"start_at"`
	EndAt   time.Time `json:"end_at"`
}

type Server struct {
	ctx       context.Context
	cancel    context.CancelFunc
	waitGroup sync.WaitGroup
	locker    sync.RWMutex
	clients   sync.Map
	conn      net.Conn
	listener  *net.TCPListener
	status    *ServerStatus
	config    *ServerConfig
	count     int32
	stop      chan interface{}
}

func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
	if config.Address == "" {
		return nil, errors.New("非法的服务器地址")
	}
	addr, err := net.ResolveTCPAddr("tcp", config.Address)
	if err != nil {
		return nil, err
	}
	config.address = addr
	if config.Config.Timeout == 0 {
		config.Timeout = time.Second * 3
	}

	debug = config.Debug

	ctx, cancel := context.WithCancel(ctx)
	return &Server{
		ctx:       ctx,
		cancel:    cancel,
		waitGroup: sync.WaitGroup{},
		clients:   sync.Map{},
		locker:    sync.RWMutex{},
		config:    config,
		status:    &ServerStatus{},
		count:     0,
		stop:      make(chan interface{}, 0),
	}, nil
}

// storeClient 保存客户端
func (s *Server) storeClient(client *Client) {
	_, ok := s.clients.Load(client.Protocol.DeviceCode)
	s.clients.Store(client.Protocol.DeviceCode, client)
	if ok {
		LoadCallsFilterCodeRun(CodeClientChangeStatus, client)
		return
	}
	atomic.AddInt32(&s.count, 1)
	LoadCallsFilterCodeRun(CodeClientAdd, client)
	return
}

// removeClient 删除客户端
func (s *Server) removeClient(deviceCode string) {
	if v, ok := s.clients.Load(deviceCode); ok {
		s.clients.Delete(deviceCode)
		atomic.AddInt32(&s.count, -1)
		LoadCallsFilterCodeRun(CodeClientRemove, v.(*Client))
	}
}

func (s *Server) Run() {
	go s.Ticker()
	defer func() {
		s.locker.Lock()
		defer s.locker.Unlock()
		s.status.Running = false
		s.status.EndAt = time.Now()
		LoadCallsFilterCodeRun(CodeServerChangeStatus, *s.status)
		s.clients.Range(func(key, value any) bool {
			client, ok := value.(*Client)
			if ok && client.GetStatus().IsConnected() {
				err := client.conn.Close()
				if err != nil {
					PushLog(W, fmt.Sprintf("断开DTS客户端<%s>失败: %s", client.conn.RemoteAddr(), err))
				}
				client.setStatus(Disconnected)
			}
			return true
		})
	}()
	retryCount := 0
	for {
		PushLog(I, fmt.Sprintf("开始创建DTS服务端<%s>...", s.config.address.String()))
		listen, err := net.ListenTCP("tcp", s.config.address)
		if err != nil {
			PushLog(E, fmt.Sprintf("创建服务端失败:%s", err))
			if retryCount < s.config.RetryCount {
				retryCount += 1
				time.Sleep(time.Second * s.config.Timeout)
				continue
			}
			return
		}

		s.locker.Lock()
		s.status.Running = true
		s.status.EndAt = time.Now()
		LoadCallsFilterCodeRun(CodeServerChangeStatus, *s.status)
		s.locker.Unlock()

		bit.DefaultEndPont = binary.LittleEndian
		PushLog(I, fmt.Sprintf("创建DTS服务端<%s>成功,等待客户端连接...", s.config.address.String()))
		s.listener = listen
		for {
			conn, err := s.listener.AcceptTCP()
			if err != nil {
				select {
				case <-s.stop:
					return
				default:
					continue
				}
			}
			PushLog(I, fmt.Sprintf("DTS客户端<%s>连接请求处理协程...", conn.RemoteAddr()))
			client := &Client{conn: conn, server: s}
			client.Protocol = NewProtocol(client, s.config.Config)
			go client.Protocol.Read()
		}
	}
}

func (s *Server) Close() {
	if s.listener != nil {
		_ = s.listener.Close()
		s.stop <- struct{}{}
		PushLog(W, fmt.Sprintf("关闭DTS服务端<%s>成功", s.config.address.String()))
		s.cancel()
		time.Sleep(time.Millisecond)
	}
}

func (s *Server) Ticker() {
	ticker := time.NewTicker(time.Minute)
	defer ticker.Stop()
	ctx, cancel := context.WithCancel(s.ctx)
	defer cancel()
	for {
		select {
		case <-ctx.Done():
			return
		case <-ticker.C:
			s.clients.Range(func(key, value any) bool {
				client := value.(*Client)
				if !client.IsConnected() && time.Now().Sub(client.At.Disconnect) > time.Minute*2 {
					s.removeClient(key.(string))
					PushLog(W, fmt.Sprintf("测温主机<%s>客户端<%s>超时两分钟,强制移除", client.conn.RemoteAddr(), client.Protocol.DeviceCode))
				}
				return true
			})
		}
	}
}

// Exec 执行命令
func (s *Server) Exec(deviceCode string, cmd []byte) error {
	v, ok := s.clients.Load(deviceCode)
	if ok {
		client := v.(*Client)
		if !client.IsConnected() {
			return errors.New(fmt.Sprintf("测温设备<%s>未连接", deviceCode))
		}
		PushLog(I, fmt.Sprintf("测温设备<%s>执行%s命令", deviceCode, cmd))
		_, err := client.conn.Write(cmd)
		if err != nil {
			return err
		}
		return nil
	}
	return errors.New(fmt.Sprintf("测温设备<%s>未连接", deviceCode))
}

func (s *Server) GetServerStatus() ServerStatus {
	s.locker.Lock()
	defer s.locker.Unlock()
	status := *s.status
	return status
}

func (s *Server) GetClientStatus(deviceCode string) (ClientStatus, error) {
	v, ok := s.clients.Load(deviceCode)
	if !ok {
		return Disconnected, errors.New("未查询到当前设备")
	}
	return v.(*Client).GetStatus(), nil
}

func (s *Server) GetAllClientStatus() map[string]ClientStatus {
	status := map[string]ClientStatus{}
	s.clients.Range(func(key, value any) bool {
		client, ok := value.(*Client)
		if ok {
			status[client.Protocol.DeviceCode] = client.GetStatus()
		} else {
			status[client.Protocol.DeviceCode] = NotConnected
		}
		return true
	})
	return status
}
